package jmine.selenium.services.factory;

import java.util.Collection;
import java.util.Collections;
import java.util.Map.Entry;
import java.util.Set;

import jmine.selenium.EnvironmentConfiguration;
import jmine.selenium.services.registry.ServicesRegistry;
import jmine.tec.di.scope.TypedScope;
import jmine.tec.services.api.ActionsEnum;
import jmine.tec.services.api.Service;
import jmine.tec.services.api.factory.ServiceCreationException;
import jmine.tec.services.api.factory.ServiceFactory;
import jmine.tec.utils.register.Receiver;

/**
 * ServiceFactory que tenta primeiramente instanciar implementações de serviço no formato do selenium, e se não for encontrado tenta
 * instanciar serviços implementados em Java. Quaisquer serviços posteriormente criados (ex: serviços de integração do Hydra) são criados
 * com o factory delegate.
 * 
 * @author Rafael Volpato
 * @author joao.enomoto (refactor)
 */
public class SeleniumServiceFactory implements Receiver<Class<?>>, ServiceFactory {

    private Set<ServicesRegistry> servicesRegistries;

    private ServiceFactory serviceFactoryDelegate;

    private EnvironmentConfiguration environmentConfiguration;

    /**
     * @param servicesRegistries the servicesRegistries to set
     */
    public void setServicesRegistries(Set<ServicesRegistry> servicesRegistries) {
        this.servicesRegistries = servicesRegistries;
    }

    /**
     * @return the servicesRegistries
     */
    public Set<ServicesRegistry> getServicesRegistries() {
        return this.servicesRegistries;
    }

    /**
     * @return the serviceFactoryDelegate
     */
    public ServiceFactory getServiceFactoryDelegate() {
        return this.serviceFactoryDelegate;
    }

    /**
     * @param serviceFactoryDelegate the serviceFactoryDelegate to set
     */
    public void setServiceFactoryDelegate(ServiceFactory serviceFactoryDelegate) {
        this.serviceFactoryDelegate = serviceFactoryDelegate;
    }

    /**
     * @param environmentConfiguration the environmentConfiguration to set
     */
    public void setEnvironmentConfiguration(EnvironmentConfiguration environmentConfiguration) {
        this.environmentConfiguration = environmentConfiguration;
    }

    /**
     * Tenta criar um serviço implementado em Selenium, se não conseguir busca pela implementação do serviço em Java.
     * 
     * @param action action
     * @param serviceName serviceName
     * @param typedScope serviceProperties
     * @return uma nova instancia do serviço criado
     * @throws ServiceCreationException se ocorrer algum erro
     */
    public Service<?> getService(ActionsEnum action, String serviceName, TypedScope typedScope) throws ServiceCreationException {
        for (ServicesRegistry registry : this.servicesRegistries) {
            Service service = registry.createService(action, serviceName);
            if (service != null) {
                if (this.environmentConfiguration != null) {
                    for (Entry<String, Object> entry : this.environmentConfiguration.getGlobalServiceProperties().entrySet()) {
                        typedScope.putDefinition(entry.getKey(), entry.getValue());
                    }
                }

                service.setServiceProperties(typedScope);
                return service;
            }
        }

        return this.serviceFactoryDelegate.getService(action, serviceName, typedScope);
    }

    /**
     * {@inheritDoc}
     */
    public Service<?> getService(Class<?> targetClass, TypedScope typedScope) throws ServiceCreationException {
        return this.serviceFactoryDelegate.getService(targetClass, typedScope);
    }

    /**
     * {@inheritDoc}
     */
    public Set<String> getServices() {
        // TODO joao.enomoto: adicionar tratamento correto
        return Collections.<String> emptySet();
    }

    /**
     * {@inheritDoc}
     */
    public void register(Collection<? extends Class<?>> registers) {
        // nada
    }
}
